package com.songcan.gateway.route.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.songcan.gateway.exception.ValidatedExceotion;
import com.songcan.gateway.properties.InitRoutesProperties;
import com.songcan.gateway.route.service.CustomRouteDefinitionService;
import com.songcan.gateway.vo.CustomRouteDefinitionVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

import static com.songcan.gateway.contants.AuthUrls.GATEWAY_ROUTE_KEY;
import static java.util.Collections.synchronizedMap;

@Service
public class CustomRouteDefinitionServiceImpl implements CustomRouteDefinitionService {


    private RedisTemplate redisTemplate;
    private InitRoutesProperties initRoutesProperties;
    private ApplicationEventPublisher applicationEventPublisher;
    private final Map<String, CustomRouteDefinitionVO> routes = synchronizedMap(
            new LinkedHashMap<String, CustomRouteDefinitionVO>());

    @Autowired
    public CustomRouteDefinitionServiceImpl(RedisTemplate redisTemplate,InitRoutesProperties initRoutesProperties,
                                            ApplicationEventPublisher applicationEventPublisher){
        this.redisTemplate = redisTemplate;
        this.initRoutesProperties = initRoutesProperties;
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Override
    public Map<String,CustomRouteDefinitionVO> findAll()  {
        if(routes.keySet().size()<1) {
            Map<String, CustomRouteDefinitionVO> customRouteDefinitionVOMap = (Map<String, CustomRouteDefinitionVO>) redisTemplate.opsForHash().entries(GATEWAY_ROUTE_KEY);
            List<RouteDefinition> list =null;
            if (Objects.isNull(customRouteDefinitionVOMap) || customRouteDefinitionVOMap.size() < 1) {
                customRouteDefinitionVOMap = new HashMap<>();
                list= initRoutesProperties.getRoutes();
            }else{
                list = new ArrayList<>();
                Iterator<Map.Entry<String, CustomRouteDefinitionVO>> iter=customRouteDefinitionVOMap.entrySet().iterator();
                while (iter.hasNext()){
                    CustomRouteDefinitionVO customRouteDefinitionVO = iter.next().getValue();
                    RouteDefinition routeDefinition = new RouteDefinition();
                    routeDefinition.setId(customRouteDefinitionVO.getId());
                    try {
                        routeDefinition.setUri(new URI(customRouteDefinitionVO.getUri()));
                    } catch (URISyntaxException e) {
                        e.printStackTrace();
                    }
                    routeDefinition.setPredicates(customRouteDefinitionVO.getPredicates());
                    routeDefinition.setFilters(customRouteDefinitionVO.getFilters());
                    routeDefinition.setOrder(customRouteDefinitionVO.getOrder());
                    list.add(routeDefinition);
                }
            }

            for (RouteDefinition routeDefinition : list) {
                String id = routeDefinition.getId();
                CustomRouteDefinitionVO customRouteDefinitionVO1 = new CustomRouteDefinitionVO(routeDefinition);
                customRouteDefinitionVOMap.put(id, customRouteDefinitionVO1);
                redisTemplate.opsForHash().put(GATEWAY_ROUTE_KEY, id, customRouteDefinitionVO1);
                routes.put(routeDefinition.getId(), customRouteDefinitionVO1);
            }
        }
         return  routes;
    }


    @Override
    public String loadRouteDefinition() {
        try {
            Map<String,CustomRouteDefinitionVO> customRouteDefinitions = findAll();
            if (customRouteDefinitions == null) {
                return "none route defined";
            }
            for (String key : customRouteDefinitions.keySet()) {
                    RouteDefinition routeDefinition = new RouteDefinition();
                    CustomRouteDefinitionVO customRouteDefinitionVO = (CustomRouteDefinitionVO) redisTemplate.opsForHash().get(GATEWAY_ROUTE_KEY,key);
                    if(Objects.nonNull(key)) {
                        routeDefinition.setId(key);
                        routeDefinition.setUri(new URI(customRouteDefinitionVO.getUri()));
                        List<PredicateDefinition> predicateDefinitions = customRouteDefinitionVO.getPredicates();
                        if (Objects.nonNull(predicateDefinitions)) {
                            routeDefinition.setPredicates(predicateDefinitions);
                        }
                        List<FilterDefinition> filterDefinitions = customRouteDefinitionVO.getFilters();
                        if (Objects.nonNull(filterDefinitions)) {
                            routeDefinition.setFilters(filterDefinitions);
                        }
                    }
                    this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            }
            return "success";
        } catch (Exception e) {
            e.printStackTrace();
            return "failure";
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CustomRouteDefinitionVO save(CustomRouteDefinitionVO customRouteDefinition)  {
        redisTemplate.opsForHash().put(GATEWAY_ROUTE_KEY,customRouteDefinition.getId(),customRouteDefinition);
        routes.put(customRouteDefinition.getId(),customRouteDefinition);
        return (CustomRouteDefinitionVO) redisTemplate.opsForHash().get(GATEWAY_ROUTE_KEY,customRouteDefinition.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(String id)  {
        redisTemplate.opsForHash().delete(GATEWAY_ROUTE_KEY,id);
        routes.remove(id);

    }

    @Override
    public CustomRouteDefinitionVO existsById(String id)  {
        return  CustomRouteDefinitionVO.class.cast(redisTemplate.opsForHash().get(GATEWAY_ROUTE_KEY,id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(String id, String uri, String predicates, String filters, Integer order, Integer enabled) throws ValidatedExceotion {
        CustomRouteDefinitionVO customRouteDefinitionVO = existsById(id);
        if(Objects.isNull(customRouteDefinitionVO)){
            throw new ValidatedExceotion("路由不存在");
        }
        customRouteDefinitionVO.setId(id);
        if(Objects.nonNull(uri)&& StrUtil.isNotEmpty(uri)){
            customRouteDefinitionVO.setUri(uri);
        }
        if(Objects.nonNull(predicates)&&StrUtil.isNotEmpty(predicates)){
            customRouteDefinitionVO.setPredicates(JSONArray.parseArray(predicates,PredicateDefinition.class));
        }
        if(Objects.nonNull(filters)&&StrUtil.isNotEmpty(filters)){
            customRouteDefinitionVO.setFilters(JSONArray.parseArray(filters,FilterDefinition.class));
        }
        if(Objects.nonNull(order)){
            customRouteDefinitionVO.setOrder(order);
        }
        if(Objects.nonNull(enabled)){
            customRouteDefinitionVO.setEnabled(enabled);
        }
        redisTemplate.opsForHash().put(GATEWAY_ROUTE_KEY,customRouteDefinitionVO.getId(),customRouteDefinitionVO);
        if(customRouteDefinitionVO.getEnabled() == 1) {
            routes.put(id, customRouteDefinitionVO);
        }else{
            routes.remove(id);
        }

    }



}
